package com.buct.coder.core.spider;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.buct.coder.model.Contest;
import com.buct.coder.model.Student;
import com.buct.coder.model.StudentContestLog;
import com.buct.coder.service.IContestService;
import com.buct.coder.service.IStudentContestLogService;
import com.buct.coder.service.IStudentService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import us.codecraft.webmagic.*;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.Selectable;

import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * Created with IntelliJ IDEA.
 *
 * @Description:
 * @Auther: xinzi
 * @Date: 2022/08/26/20:01
 */
@Component
@Slf4j
public class AtCoderSpider {

    private static final String AC_LINK = "https://atcoder.jp/users/%s/history";

    @Autowired
    private IStudentService studentService;
    @Autowired
    private IContestService contestService;
    @Autowired
    private IStudentContestLogService studentContestLogService;


    @Scheduled(cron ="0 0 0 0/2 * ?")
    @Async
    public void refreshData() {
        List<Student> students = studentService.list();
        for (Student student : students) {
            String acId = student.getAcId();
            if (StringUtils.isBlank(acId)) {
                continue;
            }
            Spider.create(new PageProcessor() {
                        @SneakyThrows
                        @Override
                        public void process(Page page) {
                            Html html = page.getHtml();
                            //解析页面内容
                            Selectable table = html.css("#history");
                            List<String> tr = table.css("tbody > tr").all();
                            List<Contest> contestList = new ArrayList<>();
                            List<StudentContestLog> studentContestLogList = new ArrayList<>();
                            for (int i = 1; i <= tr.size(); i++) {
                                String startTime = table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(1) > time","text").get();
                                String title = table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(2) > a","text").get();
                                String link = "https://atcoder.jp" +
                                        table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(2) > a","href").get();
                                String rank = table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(3) > a","text").get();
                                String performance = table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(4)","text").get();
                                String newRating = table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(5) > span","text").get();
                                String diff = table.css("tbody > tr:nth-child(" + i + ") > td:nth-child(6)","text").get();
                                Contest contest = Contest.builder()
                                        .startTime(DateFormat.getDateInstance().parse(startTime))
                                        .title(title)
                                        .link(link)
                                        .source("ac")
                                        .build();
                                StudentContestLog studentContestLog = StudentContestLog.builder()
                                        .stuId(student.getId())
                                        .contestRank(StringUtils.isNumeric(rank) ? Long.parseLong(rank) : -1)
                                        .performance(StringUtils.isNumeric(performance) ? Long.parseLong(performance) : -1)
                                        .newRating(StringUtils.isNumeric(newRating) ? Long.parseLong(newRating) : -1)
                                        .diff(diff)
                                        .build();
                                contestList.add(contest);
                                studentContestLogList.add(studentContestLog);
                            }
                            //把结果传递给pipeline
                            page.putField("contestList", contestList);
                            page.putField("studentContestLogList", studentContestLogList);
                        }

                        @Override
                        public Site getSite() {
                            return Site.me()
                                    // 设置字符编码集
                                    .setCharset("UTF-8")
                                    // 设置Http连接重试次数
                                    .setRetryTimes(10)
                                    // 设置线程休眠时间
                                    .setSleepTime(1000);
                        }
                    })
                    .addUrl(String.format(AC_LINK, acId))
                    .addPipeline((resultItems, task) -> {
                        List<Contest> contestList = resultItems.get("contestList");
                        List<StudentContestLog> studentContestLogList = resultItems.get("studentContestLogList");
                        for (int i = 0; i < contestList.size(); i++) {
                            Contest contest = contestList.get(i);
                            StudentContestLog studentContestLog = studentContestLogList.get(i);
                            String title = contest.getTitle();
                            Contest contestFromDb = contestService.getOne(
                                    new LambdaQueryWrapper<Contest>().eq(Contest::getTitle, title).eq(Contest::getSource, "nc")
                            );
                            if (contestFromDb == null) {
                                contestService.save(contest);
                            }
                            Long contestId = contestFromDb == null ? contest.getId() : contestFromDb.getId();
                            StudentContestLog contestLogFromDb = studentContestLogService.getOne(
                                    new LambdaQueryWrapper<StudentContestLog>().eq(StudentContestLog::getContestId, contestId)
                                            .eq(StudentContestLog::getStuId, student.getId())
                            );
                            if (contestLogFromDb == null) {
                                studentContestLog.setContestId(contestId);
                                studentContestLogService.save(studentContestLog);
                            }
                        }
                    })
                    .run();
        }
    }

}
